/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ec.cd.processor;

import ec.cd.common.Sensors;
import ec.cd.common.Sensors.SensorData;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

/**
 *
 * @author endri
 */
public class TraceProcessor2 extends TraceProcessor {

    private final int bufferSize = 30;

    public TraceProcessor2() {
        super();
    }

    @Override
    public double[] getAccRanges() {
        List<Float> X = new Vector<Float>();
        List<Float> Y = new Vector<Float>();
        List<Float> Z = new Vector<Float>();
        double p1, p2;
        int resMin, resMax;
        try {
            for (Sensors.SensorData sensorData : this.getAccData()) {
                // Print the content on the console
                X.add(sensorData.values[0]);
                Y.add(sensorData.values[1]);
                Z.add(sensorData.values[2]);
            }

            p1 = 0.01;
            p2 = 0.99;
            Collections.sort(X);
            Collections.sort(Y);
            Collections.sort(Z);
            resMin = (int) Math.round((p1 * X.size()) + 1 / 2f);
            resMax = (int) Math.round((p2 * X.size()) + 1 / 2f);
            System.out.println("resMin = " + resMin);
            System.out.println("resMax = " + resMax);
            System.out.println(X.size());
            double[] ranges = new double[6];
            ranges[0] = X.get(resMin - 1);
            ranges[1] = X.get(resMax - 1);
            ranges[2] = Y.get(resMin - 1);
            ranges[3] = Y.get(resMax - 1);
            ranges[4] = Z.get(resMin - 1);
            ranges[5] = Z.get(resMax - 1);
            return ranges;
        } catch (Exception e) {// Catch exception if any
            System.err.println("Error in calibration: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public ProcessingResults getProcessingResults(List<SensorData> sensorDatas, double q, double[] ranges, int indexToCheck) {
        String sensorType = "";
        List<SensorData> lastRecentData = new LinkedList<SensorData>();
        List<SensorData> lastRecentDataControlCase = new LinkedList<SensorData>(); //this is the one that will work as if no tolerance range q was selected. That's why it should be the representative of ground truth, therefore we check against this for error

        if (sensorDatas.isEmpty()) {
            return null;
        } else {
            double minSitting = ranges[0], maxSitting = ranges[1],
                    minWalking = ranges[2], maxWalking = ranges[3],
                    minRunning = ranges[4], maxRunning = ranges[5];

            int numMessagesToBeSent = 0;
            int numErrors = 0;
            int currentContext = 0, controlState = 0;
            float lastReportedValue = 0;

            if (sensorDatas.get(1).sensorType == Sensors.ST_ACCELEROMETER) {
                // transform raw data into tilt data
                sensorType = "Accelerometer";
            } else {
                sensorType = "Gyroscope";
            }
            for (SensorData sensorData : sensorDatas) {
                float[] sensorVal = sensorData.values;


                if (Math.abs(sensorVal[indexToCheck] - lastReportedValue)
                        >= Math.abs(q * lastReportedValue)) {
                    //value is taken into consideration
                    numMessagesToBeSent += 1;
                    lastReportedValue = sensorVal[indexToCheck];
                    if (lastRecentData.size() < bufferSize) {
                        lastRecentData.add(sensorData);
                    } else {
                        lastRecentData.remove(0); // remove the oldest
                        lastRecentData.add(sensorData); //add the new one
                    }
                }
                //add in the control list always
                if (lastRecentDataControlCase.size() < bufferSize) {
                    lastRecentDataControlCase.add(sensorData);
                } else {
                    lastRecentDataControlCase.remove(0); // remove the oldest
                    lastRecentDataControlCase.add(sensorData); //add the new one
                }
                //find min max in the lastRecentData so that we know the amplitude of change in the readings
                float minXLast = 0, minYLast = 0, minZLast = 0, maxXLast = 0, maxYLast = 0, maxZLast = 0;
                for (SensorData element : lastRecentData) {
                    if (element.values[0] < minXLast) {
                        minXLast = element.values[0];
                    }
                    if (element.values[1] < minYLast) {
                        minYLast = element.values[1];
                    }
                    if (element.values[2] < minZLast) {
                        minZLast = element.values[2];
                    }
                    if (element.values[0] > maxXLast) {
                        maxXLast = element.values[0];
                    }
                    if (element.values[1] > maxYLast) {
                        maxYLast = element.values[1];
                    }
                    if (element.values[2] > maxZLast) {
                        maxZLast = element.values[2];
                    }
                }
                //find min max in the control list
                float minXLastControl = 0, minYLastControl = 0, minZLastControl = 0, maxXLastControl = 0, maxYLastControl = 0, maxZLastControl = 0;
                for (SensorData element : lastRecentDataControlCase) {
                    if (element.values[0] < minXLastControl) {
                        minXLastControl = element.values[0];
                    }
                    if (element.values[1] < minYLastControl) {
                        minYLastControl = element.values[1];
                    }
                    if (element.values[2] < minZLastControl) {
                        minZLastControl = element.values[2];
                    }
                    if (element.values[0] > maxXLastControl) {
                        maxXLastControl = element.values[0];
                    }
                    if (element.values[1] > maxYLastControl) {
                        maxYLastControl = element.values[1];
                    }
                    if (element.values[2] > maxZLastControl) {
                        maxZLastControl = element.values[2];
                    }
                }

                // find context in the current data considering only X values
                if (maxXLast - minXLast <= maxSitting - minSitting) {
                    currentContext = 1;
                } else if (maxXLast - minXLast <= maxWalking - minWalking) {
                    currentContext = 2;
                } else if (maxXLast - minXLast <= maxRunning - minRunning) {
                    currentContext = 3;
                } else {
                    currentContext = 0;
                }
                
                // find context in the current data considering only X values
                if (maxXLastControl - minXLastControl <= maxSitting - minSitting) {
                    controlState = 1;
                } else if (maxXLastControl - minXLastControl <= maxWalking - minWalking) {
                    controlState = 2;
                } else if (maxXLastControl - minXLastControl <= maxRunning - minRunning) {
                    controlState = 3;
                } else {
                    controlState = 0;
                }

                if(currentContext != controlState){
                    numErrors += 1;
                }


                if (sensorData.sensorType == Sensors.ST_ACCELEROMETER) {
                    this.getReportedContextsAcc().add(currentContext);
                } else {
                    this.getReportedContextsGyr().add(currentContext);
                }
            }

            ProcessingResults processingResults = new ProcessingResults();
            processingResults.numMessagesSent = numMessagesToBeSent;
            processingResults.numTotalMessages = sensorDatas.size();
            processingResults.numErrors = numErrors;
            System.out.println("Number of messages in total: " + sensorDatas.size());
            System.out.println("Number of messages to be sent: " + numMessagesToBeSent);
            System.out.println("Errors: " + numErrors);
            processingResults.QoI = 1 - (numErrors / (double) sensorDatas.size());
            try {
                this.writeProcessingResultsToFile(sensorType, q, processingResults);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return processingResults;
        }
    }
}
